#include "TCPListener.h"

using namespace std;
using namespace efs;

TCPListener::TCPListener(int port)
	: m_port(port)
	, m_listener (0)
	, m_stop_req (false)
	, m_buff_size (1024)
	, m_wait_timeout (1)
{}

void TCPListener::printError(string func_name) const
{
	string msg = strerror(errno);
	pantheios::log_ERROR("[TCPListener]: "+ func_name + ": " + msg);
}

// this function starts main thread function "Listen()"
void * TCPListener::threadProc(void *base_class_pointer)
{
	static_cast<TCPListener*> (base_class_pointer)->waitRequest();
	return 0;
}

bool TCPListener::preparem_listener()
{
	struct sockaddr_in addr;

	// new socket for request receive
	m_listener = socket (AF_INET, SOCK_STREAM, 0);
	if (m_listener < 0)
	{
		printError("socket");
		return false;
	}

	// non-blocking mode
	fcntl(m_listener, F_SETFL, O_NONBLOCK);

	// setting address and m_port
	addr.sin_family = AF_INET;
	addr.sin_port = htons (m_port);
	addr.sin_addr.s_addr = INADDR_ANY;

	// this operation prevents "Address already in use" error
	int tr = 1;
	if (setsockopt(m_listener, SOL_SOCKET, SO_REUSEADDR, &tr, sizeof(int)) == -1)
	{
		printError("setsockopt");
		return false;
	}

	if (bind(m_listener, (struct sockaddr *)&addr, sizeof(addr)) < 0)
	{
		printError("bind");
		return false;
	}

	listen(m_listener, 2);
	return true;
} 


void TCPListener::waitRequest(void)
{
	pantheios::log_DEBUG("[TCPListener]: Waiting for connection");

	while(1)
	{
		FD_ZERO (&m_readset);
		FD_SET (m_listener, &m_readset);

		// set the timeout
		timeval timeout;
		timeout.tv_sec = 0;//m_wait_timeout; //TODO:
		timeout.tv_usec = 100 * 1000;

		if (m_stop_req)
		{
			pantheios::log_DEBUG("[TCPListener]: Exit on user command");
			closeAll();
			return;
		}

		// waiting for event in socket
		if (select(m_listener + 1, &m_readset, NULL, NULL, &timeout) <= 0)
		{
			continue;
		}

		if (FD_ISSET(m_listener, &m_readset))
		{
			Socket2 *socket = new Socket2(m_listener);

			pantheios::log_DEBUG("[TCPListener]: reading start");
			std::string request;
			const int b_size = 1024;
			char buff[b_size];
			size_t readed_all = 0;
			int readed = 0;
			bool error = false;
			do
			{
				readed = socket->read(buff, b_size);
				if (readed < 0)
				{
					error = true;
					break;
				}
				readed_all += readed;
				request += string(buff, readed);
				//std::cout << request << std::endl;
			}while(readed > 0);

			delete socket;
			//cout << readed_all << endl;

			pantheios::log_DEBUG("[TCPListener]: reading stop");

			if (!error)
			{
				pantheios::log_DEBUG("[TCPListener]: processing begin");
				m_subscriber->processTCPMessageLock(request);
			}

			//sleep(1);
			pantheios::log_DEBUG("[TCPListener]: processing end");

			pantheios::log_DEBUG("[TCPListener]: Waiting for connection");
		}
	}
}


bool TCPListener::closeAll(void)
{
	if (m_listener)
	{
		close(m_listener);
		m_listener = 0;
	}
	return true;
}

bool TCPListener::startListening(void)
{
	m_stop_req = false;	
	if (!preparem_listener())
	{
		closeAll();
		return false;
	}
	if (pthread_create (&m_thread, NULL, TCPListener::threadProc, (void*)this))
	{
		closeAll();
		return false;
	}
	return true;
}

void TCPListener::stopListening(void)
{
	m_stop_req = true;
	pthread_join(m_thread, NULL);
}

void TCPListener::setSubscriber(TCPListenerSubscriber *subscriber)
{
	m_subscriber = subscriber;
}
